在前面两节的内容中,我们一起手写了一个迷你版的 no-bundle 开发服务,也就是 Vite 开发阶段的 Dev Server,而在生产环境下面,处于页面性能的考虑,Vite 还是选择进行打包(bundle),并且在底层使用 Rollup 来完成打包的过程。在接下来的篇幅中,我们就来实现一个 JavaScript Bundler,让你理解生产环境下 Vite/Rollup 的模块打包究竟是如何实现的。
不过,需要提前声明的是,Bundler 的实现非常依赖于 AST 的实现,有相当多的地方需要解析模块 AST 并且操作 AST 节点,因此,我们有必要先完成 AST 解析的方案。目前在业界有诸多的 JavaScript AST 解析方案,如acorn、@babel/parser、swc 等,可以实现开箱即用,但为了让大家对其中的原理理解得更为深入,本小节会教大家一步步开发出 AST 的解析器,实现 tokenize 和 parse 的底层逻辑,而这本身也是一件非常有意思的事情,相信你经过本节的学习也能领略到前端编译领域的底层风光。
# 搭建开发测试环境
首先通过pnpm init -y新建项目,安装测试工具vitest:
pnpm i vitest -D
新建 src/__test__ 目录,之后所有的测试代码都会放到这个目录中。我们不妨先尝试编写一个测试文件:
// src/__test__/example.test.ts
import { describe, test, expect } from "vitest";
describe("example test", () => {
test("should return correct result", () => {
expect(2 + 2).toBe(4);
});
});
然后在package.json中增加如下的 scripts:
"test": "vitest"
接着在命令行执行 pnpm test,如果你可以看到如下的终端界面,说明测试环境已经搭建成功:

# 词法分析器开发
接下来,我们正式进入 AST 解析器的开发,主要分为两个部分来进行: 词法分析器和语法分析器。
首先是词法分析器,也叫分词器(Tokenizer),它的作用是将代码划分为一个个词法单元,便于进行后续的语法分析。比如下面的这段代码:
let foo = function() {}
在经过分词之后,代码会被切分为如下的 token 数组:
['let', 